package com.junzhiit.nengji.shop.web.util.wechat;

import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.imageio.ImageIO;
import javax.net.ssl.SSLContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

import com.alibaba.fastjson.JSON;
import com.bainaeco.framework.util.DBKey;
import com.bainaeco.framework.util.DateUtil;
import com.bainaeco.framework.util.HttpClientUtils;
import com.bainaeco.framework.util.JsonUtil;
import com.bainaeco.framework.util.PropertiesUtil;
import com.bainaeco.framework.util.SystemCodeStatus;
import com.junzhiit.nengji.model.WechatConfig;
import com.junzhiit.nengji.shop.web.util.Constants;
import com.junzhiit.nengji.shop.web.util.aliyun.AliyunOSSClientUtil;
import com.junzhiit.nengji.shop.web.util.rsa.Coder;
import com.junzhiit.nengji.shop.web.util.wechat.bean.AccessToken;
import com.junzhiit.nengji.shop.web.util.wechat.bean.LoginResponse;
import com.junzhiit.nengji.shop.web.util.wechat.bean.OrderInfo;
import com.junzhiit.nengji.shop.web.util.wechat.bean.PayParam;

public class WeChatUtils {
	
private static Log logger = LogFactory.getLog(WeChatUtils.class);
	
	private static String appid;
	
	private static String secret;
	
	private static String partner;
	
	private static String urlNotify;
	
	private static String scene;
	
	private static String paySecret;
	
	private static String SIGN_TYPE = "MD5";
	
	private static String DEFAULT_SCENE = "localhost";
	
	private static AccessToken accessToken;
	
	static {
		// 初始化微信配置
		logger.info("WeChat init start...");
		try {
			appid = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM, 
					getScene() + "." + Constants.WECHAT_KEY_FOR_APPID);
			secret = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM, 
					getScene() + "." + Constants.WECHAT_KEY_FOR_SECRET);
			partner = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM, 
					getScene() + "." + Constants.WECHAT_KEY_FOR_PARTNER);
			urlNotify = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM, 
					getScene() + "." + Constants.WECHAT_KEY_FOR_URL_NOTIFY);
			paySecret = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM, 
					getScene() + "." + Constants.WECHAT_KEY_FOR_PAY_SECRET);
			logger.info("url_notify=>"+urlNotify);
			logger.info("init secret completed.");
		} catch (IOException e) {
			e.printStackTrace();
		}
		logger.info("WeChat init end...");
	}

	private static String getScene(){
		if(StringUtils.isBlank(scene)){
			scene = DEFAULT_SCENE;
		}
		logger.info("scene=>"+scene);
		return scene;
	}
	
	public static PayParam getPayParam(OrderInfo order,WechatConfig wechatConfig){
		PayParam payParam = new PayParam();
		String timeStamp = String.valueOf(new Date().getTime());
		try {
			String nonceStr = DBKey.getUUID();
			String[] array = new String[] {
					"appid=" + wechatConfig.getAppId(),
					"mch_id=" + wechatConfig.getPartner(),
					"nonce_str=" + nonceStr,
					"body=" + order.getBody(),
					"attach=" + order.getAttach(),
					"out_trade_no=" + order.getOutTradeNo(),
					"total_fee=" + String.valueOf(order.getTotalFee()),
					"spbill_create_ip=" + order.getSpbillCreateIp(),
					"notify_url=" + urlNotify,
					"trade_type=" + order.getTradeType(),
					"openid=" + order.getOpenId(),
			};
			Arrays.sort(array);
			StringBuffer sb = new StringBuffer();
			for(int i = 0,len = array.length;i < len; i++){
				if(i !=0 ){
					sb.append("&");
				}
	            sb.append(array[i]);
	        }
			sb.append("&key=").append(wechatConfig.getPaySecret());
			logger.info(sb.toString());
			String sign = Coder.encodeByMD5(sb.toString()).toUpperCase();
			
			SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
			parameters.put("appid", wechatConfig.getAppId());
			parameters.put("mch_id", wechatConfig.getPartner());
			parameters.put("attach", order.getAttach());
			parameters.put("nonce_str", nonceStr);
			parameters.put("sign", sign);
			parameters.put("body", order.getBody());
			parameters.put("out_trade_no", order.getOutTradeNo());
			parameters.put("total_fee", String.valueOf(order.getTotalFee().intValue()));
			parameters.put("spbill_create_ip",order.getSpbillCreateIp());
			parameters.put("notify_url", urlNotify);
			parameters.put("trade_type", order.getTradeType());
			parameters.put("openid", order.getOpenId());
			
			String xml = getRequestXml(parameters);
			logger.info("xml:" + xml);
			String content = HttpClientUtils.xmlPostMethodRequest(Constants.URL_PAY_UNIFIEDORDER, xml);
			logger.info(content);
			Map<String, String> map = MessageUtil.parseXml(content);
			if(map.get("return_code").equals("SUCCESS")){
				String packages = "prepay_id=" + map.get("prepay_id");
				payParam.setAppId(wechatConfig.getAppId());
				payParam.setTimeStamp(timeStamp);
				payParam.setNonceStr(nonceStr);
				payParam.setAttach(order.getAttach());
				payParam.setPackages(packages);
				payParam.setSignType(SIGN_TYPE);
				payParam.setFormId(map.get("prepay_id"));
				array = new String[] {
						"appId=" + wechatConfig.getAppId(),
						"timeStamp=" + timeStamp,
						"nonceStr=" + nonceStr,
						"package=" + packages,
						"signType=" + SIGN_TYPE
				};
				Arrays.sort(array);
				sb = new StringBuffer();
				for(int i = 0,len = array.length;i < len; i++){
					if(i !=0 ){
						sb.append("&");
					}
		            sb.append(array[i]);
		        }
				sb.append("&key=").append(wechatConfig.getPaySecret());
				String paySign = Coder.encodeByMD5(sb.toString()).toUpperCase();
				payParam.setPaySign(paySign);
				logger.info(paySign);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		return payParam;
	}
	
	public static String getRequestXml(SortedMap<Object,Object> parameters){
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        Set<Entry<Object, Object>> es = parameters.entrySet();
        Iterator<Entry<Object, Object>> it = es.iterator();
        while(it.hasNext()) {
            Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>)it.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
                sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
            }else {
                sb.append("<"+k+">"+v+"</"+k+">");
            }
        }
        sb.append("</xml>");
        logger.info(sb.toString());
        return sb.toString();
    }
	
	public static LoginResponse authorization(String loginCode,WechatConfig wechatConfig){
		logger.error(loginCode);
		LoginResponse loginResponse = new LoginResponse();
		StringBuilder url = new StringBuilder(Constants.URL_OPENID_BY_CODE);
		url.append("?appid=").append(wechatConfig.getAppId()).append("&secret=").append(wechatConfig.getSecret()).append("&js_code=")
		.append(loginCode).append("&grant_type=").append(Constants.STRING_AUTHORIZATION_CODE);
		try {
			String content = HttpClientUtils.getMethodRequest(url.toString());
			logger.error("授权状态：--------->"+content);
			Map<String, Object> map = JsonUtil.toMap(content);
			loginResponse.setOpenId((String)map.get("openid"));
			loginResponse.setSessionKey((String)map.get("session_key"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return loginResponse;
	}
	
	public static AccessToken getAccessToken(WechatConfig wechatConfig){
		try {;
			if(accessToken == null 
					|| StringUtils.isBlank(accessToken.getAccessToken()) 
					|| accessToken.getExpiresTime() == null 
					|| (accessToken.getErrcode()!=null && accessToken.getErrcode() == Constants.CODE_STATUS_SUCCESS 
					&& accessToken.getExpiresTime()!=null && accessToken.getExpiresTime().getTime() < new Date().getTime())){
				
				StringBuilder url = new StringBuilder(Constants.URL_ACCESS_TOKEN);
				url.append("&appid=").append(wechatConfig.getAppId()).append("&secret=").append(wechatConfig.getSecret());
				logger.info("getAccessToken Url:"+url);
				try {
					accessToken =  new AccessToken();
					String content = HttpClientUtils.getMethodRequest(url.toString());
					Map<String, Object> map = JsonUtil.toMap(content);
					if(map.get("errcode") == null ){
						accessToken.setAccessToken((String)map.get("access_token"));
						int expiresIn = (Integer)map.get("expires_in");
						accessToken.setExpiresTime(DateUtil.getDateByHour(new Date(), expiresIn/60/60));
					}else {
						accessToken.setErrcode((Integer)map.get("errcode"));
						accessToken.setErrmsg((String)map.get("errmsg"));
					}
				} catch (Exception e) {
					accessToken.setErrcode(SystemCodeStatus.REQUEST_TIMEOUT);
					accessToken.setErrmsg("connect timed out...");
					logger.error(e.getMessage(), e);
				}
				logger.info("access_token:" + accessToken.getAccessToken());
			}
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		
		return accessToken;
	}
	
	/*public static void createTempQRCode(QRCode qrCode){
		AccessToken accessToken = getAccessToken();
		StringBuilder url = new StringBuilder(Constants.URL_QRCODE_CREATE);
		url.append("?access_token=").append(accessToken.getAccessToken());
		Map<String, Object> params = new HashMap<String, Object>();
		params.put("expire_seconds", qrCode.getEffectiveDay());
		params.put("action_name", "QR_SCENE");
		Map<String, Object> actionInfo = new HashMap<String, Object>();
		Map<String, Object> sceneMap = new HashMap<String, Object>();
		sceneMap.put("scene_id",qrCode.getContent());
		actionInfo.put("scene", sceneMap);
		params.put("action_info", actionInfo);
		String json = JsonUtil.toJson(params);
		logger.info(json);
		try {
			String content = HttpClientUtils.jsonPostMethodRequest(url.toString(), json);
			Map<String, Object> ticketMap = JsonUtil.toMap(content);
			String ticket = (String)ticketMap.get("ticket");
			url = new StringBuilder(Constants.URL_QRCODE_TICKET);
			url.append("?ticket=").append(ticket);
			HttpClientUtils.imageDownloader(url.toString(),qrCode.getPath());
			logger.info(content);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}*/
	
	private static String getKeyForPKCS12Path(String scene){
		return Constants.KEY_PKCS12_FOR_WECHAT.replace("{scene}", scene);
	}
	
	public static String SSLRequest(String action,String data){
		StringBuffer result = new StringBuffer();
		try {
			KeyStore keyStore = KeyStore.getInstance("PKCS12");
			String certFilePath = getKeyForPKCS12Path(scene);
			FileInputStream instream = new FileInputStream(new File(certFilePath));
			keyStore.load(instream, partner.toCharArray());
			SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, 
					partner.toCharArray()).build();
			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, 
					new String[] { "TLSv1" }, null, 
					SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
			CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
			HttpPost httpost = new HttpPost(action);
			httpost.addHeader("Connection", "keep-alive");
			httpost.addHeader("Accept", "*/*");
			httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			httpost.addHeader("Host", "api.mch.weixin.qq.com");
			httpost.addHeader("X-Requested-With", "XMLHttpRequest");
			httpost.addHeader("Cache-Control", "max-age=0");
			httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
			httpost.setEntity(new StringEntity(data, "UTF-8"));
			System.out.println("executing request" + httpost.getRequestLine());
			CloseableHttpResponse response = httpclient.execute(httpost);
			try {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					System.out.println("Response content length: " + entity.getContentLength());
					BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));
					String text;
					while ((text = bufferedReader.readLine()) != null) {
						result.append(text);
					}
				}
				EntityUtils.consume(entity);
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result.toString();
	}
	
	public static String jsonPostMethodRequest(String action, String josn) {
		String result = null;
		try {
			URL url = new URL(action);
			HttpURLConnection http = (HttpURLConnection) url.openConnection();
			http.setRequestMethod("POST");
			http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			http.setDoOutput(true);
			http.setDoInput(true);
			System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
			System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
			http.connect();
			OutputStream os = http.getOutputStream();
			os.write(josn.getBytes("UTF-8"));// 传入参数
			os.flush();
			os.close();

			InputStream is = http.getInputStream();
			int size = is.available();
			byte[] jsonBytes = new byte[size];
			is.read(jsonBytes);
			result = new String(jsonBytes, "UTF-8");
			logger.info(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public static void wxQRCode(String backetName,String code,String userId,String path,String name,WechatConfig wechatConfig){
		AccessToken accessToken = getAccessToken(wechatConfig);
		Map<String, Object> params = new HashMap<>();
		if(StringUtils.isNoneBlank(code)){
			params.put("scene",code);//参数
		}
		params.put("path", Constants.PROGRAM_HOME);//路径
		params.put("width", 430);
		logger.info("QRCode params"+params);
		CloseableHttpClient  httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(Constants.URL_QRCODE_CREATE+accessToken.getAccessToken());  // 接口
        httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
        String body = JSON.toJSONString(params); //必须是json模式的 post      
        StringEntity entity;
        try {
			entity = new StringEntity(body);
			entity.setContentType("image/png");
	        httpPost.setEntity(entity);
	        HttpResponse response;

	        response = httpClient.execute(httpPost);
	        InputStream inputStream = response.getEntity().getContent();
	        //saveToImgByInputStream(inputStream,path,name,code,userId);  //保存图片
	        AliyunOSSClientUtil.uploadObject2OSS(inputStream, name, backetName, path);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static int saveToImgByInputStream(InputStream instreams,String imgPath,String imgName,String code,String userId){
		 int stateInt = 1;
	        if(instreams != null){
	            try {
	                File file=new File(imgPath,imgName);//可以是任何图片格式.jpg,.png等
	                FileOutputStream fos=new FileOutputStream(file);
	                byte[] b = new byte[1024];
	                int nRead = 0;
	                while ((nRead = instreams.read(b)) != -1) {
	                    fos.write(b, 0, nRead);
	                }
	                fos.flush();
	                fos.close();                
	            } catch (Exception e) {
	                stateInt = 0;
	                e.printStackTrace();
	            } finally {
	            }
	        }
	        return stateInt;
    }
	
	/**
	 * 
	 * TODO 根据用户头像自定义小程序二维码
	 * qrCodePath 原图
	 * userAvatarurlPath 用户头像路径
	 * uploadPath 临时路径
	 * name 图片名称
	 * @author PeterMo
	 * @time 2018年6月5日 下午5:11:49
	 * @return void
	 */
	public static void replaceLogo(String qrCodePath,String userAvatarurlPath,String uploadPath,String name){
		 //截取为正方形图片,长宽自定义，我自己估摸着是195，这个可以根据自己情况自己设置大小  
		 try {  
		     //此处是要替代logo的自定义图片路径，自定义图片不要用透明的或者半透明的  
		     BufferedImage image = ImageIO.read(new File(userAvatarurlPath+name));  
		     BufferedImage output = new BufferedImage(195, 195, BufferedImage.TYPE_INT_ARGB);  
		     Graphics2D g2 = output.createGraphics();  
		     output = g2.getDeviceConfiguration().createCompatibleImage(195, 195, Transparency.TRANSLUCENT);  
		     g2.dispose();  
		     g2 = output.createGraphics();  
		     g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  
		     g2.fillRoundRect(0, 0,195, 195, 720, 720);  
		     g2.setComposite(AlphaComposite.SrcIn);  
		     g2.drawImage(image, 0, 0, 195, 195, null);  
		     g2.dispose();  
		     //这是生成的临时替换logo图片的保存路径                 
		     ImageIO.write(output, "png", new File(uploadPath+name));  
		  } catch (IOException e) {  
		         e.printStackTrace();  
		  }  
		  //生产新的二维码图片  
		  try{  
		     //此处是小程序码的路径  
		     BufferedImage appletImg = ImageIO.read(new FileInputStream(qrCodePath+name));  
		     Graphics2D g2d = appletImg.createGraphics();  
		   
		     // 设置抗锯齿的属性    
		     g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  
		     g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);  
		       
		     //此处是替换logo的临时图片路径  
		     BufferedImage centerImg = ImageIO.read(new File(uploadPath+name));  
		     g2d.drawImage(centerImg.getScaledInstance(centerImg.getWidth(), centerImg.getHeight(), Image.SCALE_SMOOTH), (appletImg.getWidth() - centerImg.getWidth()) / 2, (appletImg.getHeight() - centerImg.getHeight()) / 2, null);  
		   
		     // 关闭资源  
		     g2d.dispose();  
		     //生成新的二维码，覆盖原来的，此处为原小程序码路径，如需另为保存，请自定义路径  
		     ImageIO.write(appletImg, "png", new File(uploadPath+name));  
		  }catch (Exception e){  
		     e.printStackTrace();  
		  }  
		  //删除生成的临时替代logo文件                
		  //new File(uploadPath+name).delete(); 
	}
	
	
	 public static void main(String[] args) {
		 //AccessToken accessToken = getAccessToken();m
			 //wxQRCode("10005","123123", "D:\\","BB.png",accessToken);
		 
	}
	
}
